#include "RayTraceSystem.h"
#include "Basic/GxLibBasic.h"
#include "ShaderRegistry.h"
#include "StandardShaders.h"
#include "Shader.h"
#include "Scene.h"
#include "TextureData.h"
#include "Renderer.h"
#include <map>

using namespace GxLibBasic;

namespace RayTracePro
{
	class RayTraceSystemImpl : public RayTraceSystem
	{
	private:
		RefPtr<ShaderRegistry> shaderRegistry;
		std::map<String, TextureData*> textures;
		typedef std::map<String, TextureData*>::iterator TextureMapIterator;
	public:
		RayTraceSystemImpl()
		{
			DebugPrint = false;
			shaderRegistry = CreateShaderRegistry();
			RegisterStandardShaders(shaderRegistry.operator->());
			TlsId = TlsAlloc();
			InitParameters();
		}
		virtual ~RayTraceSystemImpl()
		{
			TlsFree(TlsId);
			for (TextureMapIterator iter = textures.begin(); iter != textures.end(); iter++)
				delete iter->second;
		}
		ShaderRegistry * GetShaderRegistry()
		{
			return shaderRegistry.operator->();
		}
		ShadingNode * CreateShader(const wchar_t * name)
		{
			ShadingNode * result = shaderRegistry->CreateShader(name);
			if (result) 
				result->Init(this);
			return result;
		}
		Scene * CreateScene()
		{
			return new Scene(this);
		}
		Renderer * CreateRenderer()
		{
			return new Renderer(this);
		}
		Material * CreateMaterial()
		{
			return new Material();
		}
		virtual TextureData * LoadTexture(const wchar_t * fileName)
		{
			TextureMapIterator iter = textures.find(fileName);
			if (iter == textures.end())
			{
				TextureData * rs = new TextureData(fileName);
				textures[fileName] = rs;
				return rs;
			}
			else
			{
				return iter->second;
			}
		}
		virtual void FreeTexture(TextureData * texture)
		{
			texture->RefCount --;
			if (!texture->RefCount)
			{	
				TextureMapIterator iter = textures.find(texture->FileName);
				if (iter != textures.end())
					textures.erase(iter);
				delete texture;
			}
		}
	public:
		void InitParameters()
		{
			TotalPhotons = 10000;
			PhotonTraceDepth = 5;
			PhotonsPerEstimate = 50;
			TraceDepth = 6;
			ShadowBias = 0.01f;
			ImportonResolution = 256;
			FinalGatherRays = 24;
			EnableFinalGather = true;
			TextureFilter = RayTraceSystem::TextureFilterAnisotropic;
			TextureFilterAnisotropicRate = 8;
			AmbientLight = Vec3(0.3f, 0.3f, 0.3f);
			EnableGI = false;
		}
	};

	RayTraceSystem * CreateRayTraceSystem()
	{
		return new RayTraceSystemImpl();
	}

	void FreeRayTraceSystem(RayTraceSystem * system)
	{
		delete system;
	}
}